package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {
    /**
     * 查询热点文章
     */
    @Autowired
    ApArticleMapper apArticleMapper;

    @Override
    public void hotArticle() {
        //1 . 查询近5天的文章
        //1.1 得到五天前的时间：
        String param = LocalDateTime.now().minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));

        //1。2 调用mapper查询
        List<ApArticle> apArticles = apArticleMapper.selectArticleByDate(param);

        //2 . 计算每篇文章的分值 ， 并分装为Vo集合
        List<HotArticleVo> hotArticleVos = computerHotArticle(apArticles);
        System.out.println(hotArticleVos);

        //3 . 按照频道 ， 每个频道下缓存热度最高的30条文章
        cacheTagToRedis(hotArticleVos);

    }


    //
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        //1 . 根据文章id查询文章数据
        ApArticle article = apArticleMapper.selectById(mess.getArticleId());
        if (article == null) {
            log.error("为查询到相关的文章信息");
            return;
        }
        //2 . 更新数据库文章的各个行为的值
        article.setComment((int) (article.getComment()==null ? 0 :article.getComment() +mess.getComment()));
        article.setComment((int) (article.getViews()==null ? 0 :article.getViews() +mess.getView()));
        article.setComment((int) (article.getLikes()==null ? 0 :article.getLikes() +mess.getLike()));
        article.setComment((int) (article.getCollection()==null ? 0 :article.getCollection() +mess.getCollect()));
        apArticleMapper.updateById(article);

        //3 . 计算分值
        Integer score = getSocre(article);
        //4 . 判断是否是今天发表的文章，是就文章整体权重乘以3
        String nowStr = DateUtils.dateToString(new Date());//今天
        String publishStr = DateUtils.dateToString(article.getPublishTime());//文章的发布时间
        if (nowStr.equals(publishStr)){
            score = score*3;
        }
        //5 . 查询对应频道热点文章 ， 替换分值较低的文章

        updateArticleCache(article,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId());
        //6 . 查询推荐频道热点文章 ， 替换分值较低的文章
        updateArticleCache(article,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     *
     * @param article 文章数据
     * @param score  文章的最新的分
     * @param cacheKey 要跟新缓存的key
     */
    private void updateArticleCache(ApArticle article, Integer score, String cacheKey) {
        //1 . 获取文章中的热点文章的缓存数据
        ValueOperations<String, String> valueOpr = redisTemplate.opsForValue();
        String articleVoListJson = valueOpr.get(cacheKey);

        if (StringUtils.isNotBlank(articleVoListJson)){
                boolean isHas = false;
            //2 . 判断当前的文章是否存在与热点文章中
            List<HotArticleVo> hotArticleVos = JSON.parseArray(articleVoListJson, HotArticleVo.class);
            //如果存在直接跟新热度值

            for (HotArticleVo hotArticleVo : hotArticleVos) {
                if (article.getId().equals(hotArticleVo.getId())){
                    hotArticleVo.setScore(score);//重新设置得分
                    isHas = true;
                    break;
                }
            }
            //3 . 如果文章不存在 ， 直接将文章封装成vo对象， 加入到热点文章中
            if (!isHas){
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(article,hotArticleVo);
                hotArticleVo.setScore(score);
                hotArticleVos.add(hotArticleVo);
            }
            //4 . 重新对文章进行排序， 并截取30条文章
            hotArticleVos =  hotArticleVos.stream()
                            .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                            .limit(30)
                            .collect(Collectors.toList());
            ///5 . 缓存到redis中
            valueOpr.set(cacheKey , JSON.toJSONString(hotArticleVos));
        }

    }


    @Autowired
    AdminFeign adminFeign;

    /**
     * 按照频道缓存文章 ,
     * 按热度 降序排序 ， 每个频道  只缓存热度最高的30条文章
     * 推荐频道   缓存所有文章 ，  取热度最高的文章
     *
     * @param hotArticleVos
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVos) {
        //1 . 远程调用feign接口
        ResponseResult<List<AdChannel>> channels = adminFeign.selectChannels();

        if (!channels.checkCode()) {
            log.error("远程查询出错");
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR, "远程调用频道出错");
        }
        //频道的列表
        List<AdChannel> channelList = channels.getData();

        //2 . 遍历频道 ， 再所有文章中挑选出该频道的文章 ， 调用保存方法，保存热点文章
        channelList.forEach(channel -> {
            List<HotArticleVo> ListByChannel = hotArticleVos.stream().filter(articleVo -> channel.getId().equals(articleVo.getChannelId()))
                    .collect(Collectors.toList());
            sortAndCache(ListByChannel, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channel.getId());
        });

        //3 . 缓存推荐频道 ， 调用保存方法 保存热点文章

        sortAndCache(hotArticleVos, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }



    @Autowired
    StringRedisTemplate redisTemplate;
    /**
     * 将热点文章 排序 截取前30条  缓存到指定的redis key中
     *
     * @param listByChannel
     * @param
     */
    private void sortAndCache(List<HotArticleVo> listByChannel, String CacheKey) {
        //将热点文章 排序 截取前30条
        List<HotArticleVo> hotArticleVoList = listByChannel.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        //将30条文章缓存到redis中
        redisTemplate.opsForValue().set(CacheKey , JSON.toJSONString(hotArticleVoList));

    }


    /**
     * 计算每篇文章的分值 ， 并分装为Vo集合
     *
     * @param apArticles
     * @return
     */

    private List<HotArticleVo> computerHotArticle(List<ApArticle> apArticles) {
        //1. 循环遍历文章列表
        return apArticles.stream().map(article -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(article, hotArticleVo);
            Integer score = getSocre(article);
            hotArticleVo.setScore(score);
            return hotArticleVo;
        }).collect(Collectors.toList());
    }

    /**
     * 计算文章的热度得分
     *
     * @param article
     * @return
     */
    private Integer getSocre(ApArticle article) {
        //按照权重计算得分
        int score = 0;
        // 阅读 1
        if (article.getViews() != null) {
            score += article.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (article.getLikes() != null) {
            score += article.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (article.getComment() != null) {
            score += article.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (article.getCollection() != null) {
            score += article.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }


}
